package space.edge.d.demo.qrcode.codescanner.ui;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;

import space.edge.d.demo.qrcode.R;

/**
 * Created by gsl on 2017/11/6.
 * 扫描view
 */

public class HollowView extends View implements ValueAnimator.AnimatorUpdateListener {

    private final int STATE_SCANNING = 0x01;
    private final int STATE_STOPPED = 0x00;

    private Paint paint;
    private PorterDuffXfermode porterDuffXfermode;
    private Rect hollowRect;

    private int cornerColor;
    private int cornerLength;
    private int cornerSize;
    private int borderWidth;
    private int borderColor;
    private int hollowWidth;
    private int hollowHeight;
    private float hollowWidthProportion;
    private float hollowHeightProportion;
    private int hollowTopOffset;
    private boolean hollowCenterVertical;
    private Drawable scannerLine;
    private Bitmap scannerLineBitmap;
    private int scannerLineStartY;

    private int state;
    private ValueAnimator animator;
    private long animationPlayTime;


    public HollowView(Context context) {
        super(context);
        init(context, null);
    }

    public HollowView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public HollowView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        paint = new Paint();
        paint.setAntiAlias(true);
        porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
        initAttrs(context, attrs);
    }

    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HollowView);
        cornerColor = ta.getColor(R.styleable.HollowView_corner_color, 0);
        cornerLength = ta.getDimensionPixelSize(R.styleable.HollowView_corner_length, 0);
        cornerSize = ta.getDimensionPixelSize(R.styleable.HollowView_corner_size, 0);
        hollowWidth = ta.getDimensionPixelSize(R.styleable.HollowView_hollow_width, 0);
        hollowHeight = ta.getDimensionPixelSize(R.styleable.HollowView_hollow_height, 0);
        hollowWidthProportion = ta.getFloat(R.styleable.HollowView_hollow_width_proportion, 0f);
        hollowHeightProportion = ta.getFloat(R.styleable.HollowView_hollow_height_proportion, 0f);
        hollowCenterVertical = ta.getBoolean(R.styleable.HollowView_hollow_center_vertical, true);
        hollowTopOffset = ta.getDimensionPixelSize(R.styleable.HollowView_hollow_top_offset, 0);
        borderWidth = ta.getDimensionPixelSize(R.styleable.HollowView_border_width, 0);
        borderColor = ta.getColor(R.styleable.HollowView_border_color, 0);
        scannerLine = ta.getDrawable(R.styleable.HollowView_scanner_line);
        ta.recycle();
    }

    public void startScan() {
        if (state == STATE_SCANNING)
            return;
        state = STATE_SCANNING;
        if (hollowRect != null)
            initAnimatorAndStart();
    }

    public void stopScan() {
        if (state == STATE_STOPPED)
            return;
        state = STATE_STOPPED;
        if (animator != null)
            animator.cancel();
    }

    public Rect getHollowRect() {
        if(hollowRect==null)
            return null;
        return new Rect(hollowRect);
    }

    private void initAnimatorAndStart() {
        if (animator != null && animator.isRunning())
            animator.cancel();
        animator = ValueAnimator.ofInt(hollowRect.top + cornerSize, hollowRect.bottom - scannerLineBitmap.getHeight() - cornerSize);
        animator.setDuration(2000);
        animator.setRepeatMode(ValueAnimator.RESTART);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.addUpdateListener(this);
        if (animationPlayTime > 0)
            animator.setCurrentPlayTime(animationPlayTime);
        animator.start();
    }

    private void initScannerLine() {
        if (scannerLine != null) {
            Bitmap bitmap = ((BitmapDrawable) scannerLine).getBitmap();
            Matrix scaleMatrix = new Matrix();
            float scale = hollowWidth * 1.0f / bitmap.getWidth();
            scaleMatrix.setScale(scale, scale);
            scannerLineBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), scaleMatrix, true);
        }
    }

    private void calculateHollowSize(int w) {
        if (hollowWidth == 0)
            if (hollowWidthProportion > 0) {
                hollowWidth = Math.round(w * hollowWidthProportion);
            }

        if (hollowHeight == 0)
            if (hollowHeightProportion > 0)
                hollowHeight = Math.round(hollowWidth * hollowHeightProportion);
        initScannerLine();
    }

    private void calculateHollowRect(int w, int h) {
        int left = (w - hollowWidth) / 2;
        int top = hollowCenterVertical ? (h - hollowHeight) / 2 : hollowTopOffset;
        hollowRect = new Rect(left, top, left + hollowWidth, top + hollowHeight);
        scannerLineStartY = hollowRect.top;
    }

    private void drawCoverAndHollow(Canvas canvas) {

//        canvas.saveLayer(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint,Canvas.ALL_SAVE_FLAG);
        paint.reset();
        canvas.drawColor(getResources().getColor(R.color.color_translucent));
        paint.setXfermode(porterDuffXfermode);
        paint.setColor(getResources().getColor(R.color.color_transparent));
        canvas.drawRect(hollowRect, paint);
        paint.setXfermode(null);
//        canvas.restore();
    }

    private void drawBorder(Canvas canvas) {
        if (borderWidth == 0)
            return;
        paint.setColor(borderColor);
        paint.setStrokeWidth(borderWidth);
        paint.setStyle(Paint.Style.STROKE);
        float offset = borderWidth / 2f;
        RectF rectF = new RectF(hollowRect.left + offset, hollowRect.top + offset, hollowRect.right - offset, hollowRect.bottom - offset);
        canvas.drawRect(rectF, paint);
    }

    private void drawCornerLine(Canvas canvas) {
        if (cornerSize == 0 || cornerLength == 0)
            return;
        paint.setColor(cornerColor);
        paint.setStrokeWidth(cornerSize);
        paint.setStrokeCap(Paint.Cap.SQUARE);

        float offset = cornerSize / 2f;
        float leftStartX = hollowRect.left + offset;
        float topStartY = hollowRect.top + offset;
        float rightStartX = hollowRect.right - offset;
        float bottomStartY = hollowRect.bottom - offset;
        canvas.drawLine(leftStartX, topStartY, leftStartX + cornerLength, topStartY, paint);
        canvas.drawLine(leftStartX, topStartY, leftStartX, topStartY + cornerLength, paint);
        canvas.drawLine(rightStartX, topStartY, rightStartX - cornerLength, topStartY, paint);
        canvas.drawLine(rightStartX, topStartY, rightStartX, topStartY + cornerLength, paint);
        canvas.drawLine(leftStartX, bottomStartY, leftStartX + cornerLength, bottomStartY, paint);
        canvas.drawLine(leftStartX, bottomStartY, leftStartX, bottomStartY - cornerLength, paint);
        canvas.drawLine(rightStartX, bottomStartY, rightStartX - cornerLength, bottomStartY, paint);
        canvas.drawLine(rightStartX, bottomStartY, rightStartX, bottomStartY - cornerLength, paint);
    }

    private void drawScannerLine(Canvas canvas) {
        if (scannerLine == null || scannerLineBitmap == null)
            return;
        canvas.drawBitmap(scannerLineBitmap, hollowRect.left, scannerLineStartY, paint);
    }

    private void pauseAnimator() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            animationPlayTime = animator.getCurrentPlayTime();
            animator.cancel();
        } else {
            animator.pause();
        }
    }

    private void resumeAnimator() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            initAnimatorAndStart();
        } else {
            animator.resume();
        }
    }

    @Override
    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        if (visibility == View.INVISIBLE || visibility == View.GONE) {
            if (animator != null && animator.isRunning()) {
                pauseAnimator();
            }
        } else if (state == STATE_SCANNING && hollowRect != null) {
            resumeAnimator();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        calculateHollowSize(w);
        calculateHollowRect(w, h);
        if (state == STATE_SCANNING && animator == null)
            initAnimatorAndStart();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        drawCoverAndHollow(canvas);
        drawBorder(canvas);
        drawCornerLine(canvas);
        drawScannerLine(canvas);
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        scannerLineStartY = (int) animation.getAnimatedValue();
        if (state == STATE_SCANNING)
            postInvalidateDelayed(30, hollowRect.left, scannerLineStartY, hollowRect.right, scannerLineStartY + scannerLineBitmap.getHeight());
        else
            animator.cancel();
    }
}
